package com.umeox.watch.moto.chat.db

import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.SoundPool
import android.os.*
import android.text.TextUtils
import android.util.Log
import com.umeox.moto.common.log.Logger
import com.umeox.moto.common.utils.FileUtils
import com.umeox.moto.common.utils.StringUtils
import com.umeox.watch.moto.chat.R
import com.umeox.watch.moto.chat.base.BaseApp
import com.umeox.watch.moto.chat.proto.ChatMsgProtos
import com.umeox.watch.moto.chat.utils.Utility
import com.umeox.watch.moto.dataservice.DataProvider
import com.umeox.watch.moto.dataservice.interfaces.IChat
import com.umeox.watch.moto.dataservice.proxy.ChatProxy
import java.io.File
import java.lang.Exception
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.max
import kotlin.math.min

/**
 * Created by Rambo 2021/8/19
 * Describe:
 */
class MessageDelegate {

    private val MSG_NOTIFY = 4
    private val CPU_COUNT = Runtime.getRuntime().availableProcessors()

    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private val CORE_POOL_SIZE = max(2, min(CPU_COUNT - 1, 4))
    private val MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1
    private val KEEP_ALIVE_SECONDS = 30L

    private lateinit var chatThreadPoolExecutor: ThreadPoolExecutor

    //当前聊天的对象
    private var target: ContactInfo? = null
    private var sml: MessageHandlerListener? = null
    private lateinit var mHandler: Handler
    private val count = AtomicInteger()
    private val queue: BlockingQueue<Int> = LinkedBlockingQueue()
    private lateinit var sp: SoundPool

    @Volatile
    private var readied = false
    private var mVibrator: Vibrator
    private var mChatMessageDao: ChatMessageDao
    private var mContactInfoDao: ContactInfoDao

    init {
        mChatMessageDao = AppDatabase.get().chatMessageDao()
        mContactInfoDao = AppDatabase.get().contactInfoDao()
        chatThreadPoolExecutor = ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            LinkedBlockingQueue(128),
            object : ThreadFactory {
                private val count = AtomicInteger(0)
                override fun newThread(r: Runnable): Thread {
                    val thread = Thread(r)
                    thread.name = "ChatMsgTask #" + count.addAndGet(1)
                    return thread
                }
            })
        val mExecutor = HandlerThread("MessageDelegate")
        mExecutor.start()
        mHandler = MessageHandler(mExecutor.looper)
        sp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            SoundPool.Builder()
                .setMaxStreams(1)
                .setAudioAttributes(
                    AudioAttributes.Builder()
                        .setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
                        .build()
                )
                .build()
        } else {
            SoundPool(1, AudioManager.STREAM_MUSIC, 0)
        }
        sp.load(BaseApp.getContext(), R.raw.new_msg, 1)
        sp.setOnLoadCompleteListener { _: SoundPool?, _: Int, _: Int ->
            readied = true
            if (count.get() > 0) {
                mHandler.sendEmptyMessage(MSG_NOTIFY)
            }
        }
        mVibrator =
            BaseApp.getContext().applicationContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
    }

    companion object {
        fun getManager(): MessageDelegate {
            return Holder.manager
        }
    }


    fun registerMessageListener(sml: MessageHandlerListener?, target: ContactInfo?) {
        this.sml = sml
        this.target = target
    }

    fun unRegisterMessageListener() {
        sml = null
        target = null
    }

    /**
     * 将发送的语聊保存到数据库
     */
    fun saveOutMessage(contact: ContactInfo?, chatInfo: ChatMessage, isResend: Boolean): Long {
        val rowId: Long
        try {
            //重新发送
            if (chatInfo.id > 0 && mChatMessageDao.update(chatInfo) > 0) {
                rowId = chatInfo.id
                if (sml != null) {
                    sml!!.onSendMessage(chatInfo, isResend)
                }
            } else {
                rowId = mChatMessageDao.save(chatInfo)
//                chatInfo.id = rowId
                if (sml != null) {
                    sml!!.onMessageSending(chatInfo)
                }
            }
            //修改联系人最后一次聊天信息
            updateLastContactInfo(contact, chatInfo)
        } catch (e: Exception) {
            e.printStackTrace()
            return -1
        }
        return rowId
    }

    /**
     * 将收到的语聊保存到数据库
     */
    @Synchronized
    private fun saveInMessage(contact: ContactInfo, chatInfo: ChatMessage): Boolean {
        try {
            //标记是否需要增加未读数量
            val unReadFlag: Boolean =
                if (sml != null && target!!.friendId == (if (chatInfo.type == 1) chatInfo.tuid else chatInfo.fuid)) {
                    Logger.i("正在和[" + chatInfo.fuid + "]聊天")
                    //targetUID不为空说明用户在聊天界面，并且targetUID和Fuid相同，表示正在和该好友聊天
                    chatInfo.msgType == ChatMsgProtos.ChatMsgType.VOICE_VALUE
                } else {
                    Logger.i("不在聊天页面，标记未读")
                    true
                }

            chatInfo.unRead =
                (if (unReadFlag) ChatMsgProtos.ChatMsgState.STATE_UNREAD_VALUE else ChatMsgProtos.ChatMsgState.STATE_READ_VALUE)
            val id = mChatMessageDao.queryIdByMsgId(chatInfo.msgId)
            val affectedRows: Long
            if (id > 0) {
                chatInfo.id = id
                affectedRows = mChatMessageDao.update(chatInfo).toLong()
                Logger.i("update incoming message " + if (affectedRows > 0) "success" else "failure")
                //在语聊界面，通知页面刷新
                sml?.onGetMessage(chatInfo, true)
            } else {
                affectedRows = mChatMessageDao.save(chatInfo)
                Logger.i("insert incoming message " + if (affectedRows > 0) "success" else "failure")
                //插入新的聊天信息，需要发送响铃通知
                if (affectedRows > 0) {
                    chatInfo.id = affectedRows
                    //修改联系人最后一次聊天信息以及未读消息数量
                    increaseUnreadNumber(contact, chatInfo)
                    if (queue.offer(count.incrementAndGet())) {
                        mHandler.sendEmptyMessage(MSG_NOTIFY)
                    }
                    //在语聊界面，通知页面刷新
                    sml?.onGetMessage(chatInfo, false)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
        return true
    }

    /**
     * 收到的语聊文件有未读标记
     * 每点击一条，在数据库相应的未读标记数量减1
     */
    fun setReadState(contactId: Long, rawId: Long) {
        try {
            val effected =
                mChatMessageDao.updateUnreadById(rawId, ChatMsgProtos.ChatMsgState.STATE_READ_VALUE)
            Logger.i("标记消息已读->$effected")
            if (effected > 0) {
                val bean: ContactInfo? = mContactInfoDao.queryById(contactId)
                if (bean != null) {
                    if (bean.missCount - 1 <= 0) {
                        bean.missCount = 0
                    } else {
                        bean.missCount = bean.missCount - 1
                    }
                    mContactInfoDao.save(bean)
                    //通知监听器，数据已经改变
                    DataProvider.ChatContactProvider.notifyChange()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 进入与好友聊天界面，将该好友的未读消息(语聊除外)全部标记为已读
     */
    fun updateReadStateWithoutVoiceChat(contactInfo: ContactInfo?) {
        try {
            if (contactInfo != null) {
                mChatMessageDao.updateReadStateWithoutVoiceType(
                    contactInfo.friendId,
                    ChatMsgProtos.ChatMsgState.STATE_READ_VALUE
                )
                //获取未读消息条数
                val unReadCount = mChatMessageDao.queryUnReadCount(
                    ChatMsgProtos.ChatMsgState.STATE_UNREAD_VALUE,
                    contactInfo.friendId
                )
                if (unReadCount > 0) {
                    contactInfo.missCount = unReadCount
                } else {
                    contactInfo.missCount = 0
                }
                mContactInfoDao.save(contactInfo)
                //通知监听器，数据已经改变
                DataProvider.ChatContactProvider.notifyChange()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 修改联系人最后一次聊天时间、消息ID和未读消息数量
     *
     * @param contact  当前语聊联系人
     * @param chatInfo 当前聊天信息
     */
    private fun updateLastContactInfo(contact: ContactInfo?, chatInfo: ChatMessage) {
        val contactInfo: ContactInfo? = mContactInfoDao.queryById(contact!!.id)
        if (contactInfo != null) {
            if (!TextUtils.isEmpty(chatInfo.msgId)) {
                contactInfo.lastMessageId = chatInfo.msgId
            }
            contactInfo.lastContactTime = chatInfo.msgTime
            mContactInfoDao.save(contactInfo)
        }
    }

    /**
     * 修改好友未读消息数量
     */
    private fun increaseUnreadNumber(contact: ContactInfo, chatInfo: ChatMessage) {
        val contactInfo: ContactInfo? = mContactInfoDao.queryById(contact.id)
        if (contactInfo != null) {
            if (!TextUtils.isEmpty(chatInfo.msgId)) {
                contactInfo.lastMessageId = chatInfo.msgId
            }
            contactInfo.lastContactTime = chatInfo.msgTime
            if (chatInfo.unRead == ChatMsgProtos.ChatMsgState.STATE_UNREAD_VALUE) {
                contactInfo.missCount = contactInfo.missCount + 1
            }
            mContactInfoDao.save(contactInfo)
            //通知监听器，数据已经改变
            DataProvider.ChatContactProvider.notifyChange()
        }
    }

    /**
     * 更改聊天消息状态：发送失败、下载失败等
     */
    fun updateChatMessageState(chatMessage: ChatMessage, state: ChatMsgProtos.ChatMsgState) {
        try {
            chatMessage.state = state.number
            val affected = mChatMessageDao.updateChatMessageState(state.number, chatMessage.id)
            if (affected > 0 && sml != null) {
                //在语聊界面，通知页面刷新
                sml!!.onGetMessage(chatMessage, true)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Logger.e("update chat message state failure:" + e.message)
        }
    }

    /**
     * 服务器下发消息过来，需要从本地取出联系人ID，然后才能找到对应的聊天数据表
     */
    fun handleIncomingMessage(messges: ByteArray) {
        try {
            var chatBinder: IChat? = null
            if (chatBinder == null) {
                chatBinder = ChatProxy.fetchBinder()
            }
            val chatMsgs: List<ChatMsgProtos.ChatMsg> =
                ChatMsgProtos.ChatMsgs.parseFrom(messges).msgList
            for (chatMsg in chatMsgs) {
                val chatMessage: ChatMessage = mergeFrom(chatMsg)
                Log.d("chat_msg", "run: $chatMessage")
                val mContact: ContactInfo? =
                    mContactInfoDao.queryByFriendId(if (chatMsg.imType.number == 1) chatMsg.tid else chatMsg.fromMemberId)
                if (mContact != null) {
                    //保存聊天消息
                    saveInMessage(mContact, chatMessage)
                    if (chatMessage.msgType != ChatMsgProtos.ChatMsgType.VOICE_VALUE
                        && chatMessage.msgType != ChatMsgProtos.ChatMsgType.VIDEO_VALUE
                    ) {
                        sendNewMsgNotifyBroadCast(mContact, chatMessage)
                    }
                    //根据消息类型，判断是否需要下载
                    if (chatMessage.msgType == ChatMsgProtos.ChatMsgType.PHOTO_VALUE
                        || chatMessage.msgType == ChatMsgProtos.ChatMsgType.VOICE_VALUE
                        || chatMessage.msgType == ChatMsgProtos.ChatMsgType.VIDEO_VALUE
                    ) {
                        val fileUrl: String = chatMessage.msgBody
                        if (!TextUtils.isEmpty(fileUrl)) {
                            chatThreadPoolExecutor.execute(
                                ReloadMessageTask(
                                    chatBinder,
                                    mContact, chatMessage
                                )
                            )
                        }
                    }
                } else {
                    Logger.i("未找到对应的联系人->")
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }


    private fun sendNewMsgNotifyBroadCast(contact: ContactInfo, chatMessage: ChatMessage) {
        val intent = Intent()
        intent.action = "com.wherecom.intent.action.NOTIFY_CHAT_MSG"
        intent.putExtra("uid", if (chatMessage.type == 1) chatMessage.tuid else chatMessage.fuid)
        intent.putExtra("name", contact.name)
        intent.putExtra("message", chatMessage.msgBody)
        intent.putExtra("avatar", contact.avatar)
        intent.putExtra("timestamp", chatMessage.msgTime)
        intent.putExtra("msgType", chatMessage.msgType)
        intent.putExtra("type", chatMessage.type)
        BaseApp.getContext().sendBroadcast(intent)
    }


    private fun mergeFrom(chatMsg: ChatMsgProtos.ChatMsg): ChatMessage {
        return ChatMessage(
            0,
            chatMsg.msgId,
            chatMsg.fromMemberId,
            chatMsg.tid,
            type = chatMsg.imType.number,
            msgType = chatMsg.msgType.number,
            msgTime = chatMsg.msgTime,
            msgBody = chatMsg.msgBody,
            filePath = "",
            length = 0,
            state = 0,
            unRead = 1
        )
    }

//    private fun sendNewMsgNotifyBroadCast(contact: ContactInfo, chatMessage: ChatMessage) {
//        val intent = Intent()
//        intent.action = "com.wherecom.intent.action.NOTIFY_CHAT_MSG"
//        intent.putExtra("uid", chatMessage.fuid)
//        intent.putExtra("name", contact.name)
//        intent.putExtra("message", chatMessage.msgBody)
//        intent.putExtra("avatar", contact.avatar)
//        intent.putExtra("timestamp", chatMessage.msgTime)
//        intent.putExtra("msgType", chatMessage.msgType)
//        BaseApp.getContext().sendBroadcast(intent)
//    }

    /**
     * 本地发消息出去
     */
    fun handleOutgoingMessage(
        binder: IChat?,
        contactInfo: ContactInfo?,
        chatMessage: ChatMessage,
        isResend: Boolean
    ) {
        Logger.i("handleOutgoingMessage-->binder:$binder\r\nchatMessage:$chatMessage")
        if (binder != null) {
            //服务连接，调用接口发送
            chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_SENDING_VALUE
            chatMessage.msgId = mChatMessageDao.getMaxMsgId() ?: "0"
            chatMessage.msgTime = mChatMessageDao.getMaxMsgTime() ?: System.currentTimeMillis()
            val id = saveOutMessage(contactInfo, chatMessage, isResend)
            chatMessage.id = id
            Logger.e("id-->$id,msgId-->${chatMessage.msgId}")
            chatThreadPoolExecutor.execute(
                SendMessageTask(
                    binder,
                    contactInfo,
                    chatMessage,
                    isResend
                )
            )
        } else {
            //远程服务未连接，发送失败
            chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_SEND_FAILED_VALUE
            saveOutMessage(contactInfo, chatMessage, isResend)
        }
    }

    /**
     * 语聊下载失败，需要重新下载
     */
    fun reloadMessage(binder: IChat?, contactInfo: ContactInfo, chatMessage: ChatMessage?) {
        if (chatMessage == null) {
            return
        }
        if (binder != null) {
            //服务连接，调用接口发送
            updateChatMessageState(chatMessage, ChatMsgProtos.ChatMsgState.STATE_DOWNLOADING)
            chatThreadPoolExecutor.execute(ReloadMessageTask(binder, contactInfo, chatMessage))
        } else {
            //远程服务未连接，下载失败
            updateChatMessageState(chatMessage, ChatMsgProtos.ChatMsgState.STATE_DOWNLOAD_FAILED)
        }
    }

    /**
     * 取消正在发送或者下载消息，可以让用户重新操作
     */
    fun cancelProcessing(chatMessage: ChatMessage, target: ChatMsgProtos.ChatMsgState) {
        updateChatMessageState(chatMessage, target)
    }

    /**
     * 刷新聊天消息
     *
     * @param contact 联系人
     */
    fun refreshMessage(contact: ContactInfo?) {
        if (contact != null) {
            chatThreadPoolExecutor.execute(GetMessageTask(contact))
        }
    }


    interface MessageHandlerListener {
        fun onMessageSending(target: ChatMessage)
        fun onSendMessage(target: ChatMessage, isResend: Boolean)
        fun onGetMessage(target: ChatMessage, isUpdate: Boolean)
    }

    private object Holder {
        val manager = MessageDelegate()
    }

    private inner class MessageHandler(looper: Looper) : Handler(looper) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_NOTIFY -> if (readied) {
                    while (true) {
                        val temp: Int? = queue.poll()
                        if (temp != null) {
                            if (DataProvider.inForbidPeriodNow()) {
                                mVibrator.vibrate(200)
                                return
                            }
                            when (DataProvider.getAudioTypes()) {
                                0 -> {
                                    sp.play(1, 1f, 1f, 1, 0, 1f)
                                    mVibrator.vibrate(200)
                                }

                                1 -> mVibrator.vibrate(200)
                                2 -> sp.play(1, 1f, 1f, 1, 0, 1f)
                                3 -> {
                                }
                            }
                        } else {
                            count.set(0)
                            break
                        }
                    }
                } else {
                    sendEmptyMessageDelayed(0, 200)
                }

                else -> {
                }
            }
        }
    }

    private inner class ReloadMessageTask(
        private val daspBinder: IChat?,
        private val contact: ContactInfo,
        private val chatMessage: ChatMessage
    ) : Runnable {
        override fun run() {
            try {
                val path: String = FileUtils.getChatsCacheDir(chatMessage.fuid)
                val suffix: String = StringUtils.getSuffix(chatMessage.msgBody)
                val fileName = String.format(
                    "%1\$s_%2\$s.%3\$s", Utility.processName(
                        chatMessage.fuid
                    ), chatMessage.msgTime, if (TextUtils.isEmpty(suffix)) "amr" else suffix
                )
                val filePath = path + File.separator + fileName
                Logger.i("下载语聊filePath>>>$filePath")
                val downloadResult = daspBinder!!.downloadFile(chatMessage.msgBody, filePath)
                Logger.i("下载语聊文件>>>$downloadResult")
                if (FileUtils.isFileExists(filePath)) {
                    val length = FileUtils.getFileDuration(filePath)
                    chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_DOWNLOAD_VALUE
                    chatMessage.filePath = filePath
                    chatMessage.length = length
                } else {
                    chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_DOWNLOAD_FAILED_VALUE
                }
                if (downloadResult && saveInMessage(contact, chatMessage)) {
                    sendNewMsgNotifyBroadCast(contact, chatMessage)
                }
            } catch (e: Exception) {
                Logger.e("下载语聊文件失败：$e")
                onFailed()
            }
        }

        private fun onFailed() {
            if (chatMessage.id > 0) {
                updateChatMessageState(
                    chatMessage,
                    ChatMsgProtos.ChatMsgState.STATE_DOWNLOAD_FAILED
                )
            } else {
                chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_DOWNLOAD_FAILED_VALUE
                saveInMessage(contact, chatMessage)
            }
        }
    }

    private inner class SendMessageTask(
        private val chatBinder: IChat,
        private val contact: ContactInfo?,
        private val chatMessage: ChatMessage,
        private val isResend: Boolean
    ) : Runnable {
        override fun run() {
            try {
                var msgIdAndMsgTime = ""
                if (chatMessage.msgType == ChatMsgProtos.ChatMsgType.VOICE_VALUE
                    || chatMessage.msgType == ChatMsgProtos.ChatMsgType.PHOTO_VALUE
                    || chatMessage.msgType == ChatMsgProtos.ChatMsgType.VIDEO_VALUE
                ) {
                    //需要先上传文件
                    val fileUrl = chatBinder.uploadFile(chatMessage.filePath)
                    if (!TextUtils.isEmpty(fileUrl)) {
                        chatMessage.msgBody = fileUrl
                        msgIdAndMsgTime = chatBinder.sendChatMsg(
                            chatMessage.fuid,
                            chatMessage.tuid,
                            chatMessage.type,
                            chatMessage.msgTime,
                            chatMessage.msgType,
                            chatMessage.msgBody
                        )
                    }
                } else if (chatMessage.msgType == ChatMsgProtos.ChatMsgType.TEXT_VALUE
                    || chatMessage.msgType == ChatMsgProtos.ChatMsgType.SMALL_EXPRESSION_VALUE
                    || chatMessage.msgType == ChatMsgProtos.ChatMsgType.STICKER_VALUE
                ) {
                    //调用发送接口
                    msgIdAndMsgTime = chatBinder.sendChatMsg(
                        chatMessage.fuid,
                        chatMessage.tuid,
                        chatMessage.type,
                        chatMessage.msgTime,
                        chatMessage.msgType,
                        chatMessage.msgBody
                    )
                }
                Logger.e("msgIdAndMsgTime-->$msgIdAndMsgTime")
                if (!TextUtils.isEmpty(msgIdAndMsgTime)) {
                    val msgIdArr = msgIdAndMsgTime.split("#")
                    val msgId = msgIdArr[0]
                    val msgTime = msgIdArr[1].toLong() * 1000L
                    chatMessage.msgId = msgId
                    if (msgTime > 0) {
                        chatMessage.msgTime = msgTime
                    }
                    chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_SENT_VALUE
                } else {
                    chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_SEND_FAILED_VALUE
                }
                saveOutMessage(contact, chatMessage, isResend)
            } catch (e: Exception) {
                Logger.e("发送语聊消息失败：$e")
                //更新状态
                onFailed()
            }
        }

        private fun onFailed() {
            if (chatMessage.id > 0) {
                updateChatMessageState(chatMessage, ChatMsgProtos.ChatMsgState.STATE_SEND_FAILED)
            } else {
                chatMessage.state = ChatMsgProtos.ChatMsgState.STATE_SEND_FAILED_VALUE
                saveOutMessage(contact, chatMessage, isResend)
            }
        }
    }

    private inner class GetMessageTask(private val contact: ContactInfo) : Runnable {
        private var chatBinder: IChat? = null
        override fun run() {
            Logger.i("刷新好友聊天消息")
            if (chatBinder == null) {
                chatBinder = ChatProxy.fetchBinder()
            }
        }
    }
}